/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sle_connect_service.h"
#include "sle_connect_hal.h"
#include "sle_hilog.h"

namespace OHOS {
namespace NearLink {
namespace SleStandard {

class SleConnectService::SleConnectServiceCallback {
public:
    SleConnectServiceCallback() = default;
    ~SleConnectServiceCallback() = default;

    static void OnConnectStateChange(uint16_t connectId, const SleDeviceAddress *address,
        SleAcbState state, SlePairState pairState, SleDisConnectReason reason);
    static void OnConnectParamUpdateRequest(uint16_t connectId, SleHalErrorCode errCode, const SleConnectParam *param);
    static void OnConnectParamUpdateComplete(
        uint16_t connectId, SleHalErrorCode errCode, const SleConnectionParamUpdateEvt *param);
    static void OnAuthComplete(
        uint16_t connectId, const SleDeviceAddress *address, SleHalErrorCode errCode, const SleAuthInfo *info);
    static void OnPairComplete(uint16_t connectId, const SleDeviceAddress *address, SleHalErrorCode errCode);
    static void OnReadRssiComplete(uint16_t connectId, int8_t rssi, SleHalErrorCode errCode);
    static void OnSetPhyParamComplete(uint16_t connectId, SleHalErrorCode errCode, const SleConnectionPhyParam *param);
    static SleConnectHalCallbacks *BuildHalConnectCallback();

private:
    static SleConnectHalCallbacks halCallback_;
};

void SleConnectService::SleConnectServiceCallback::OnConnectStateChange(uint16_t connectId,
    const SleDeviceAddress *address, SleAcbState state, SlePairState pairState, SleDisConnectReason reason)
{
    auto connService = SleConnectService::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(connService, "connect service instance null error!");
    SLE_ASSERT_AND_RETURN_LOG(connService->callback_, "connect service callback null error!");
    SleAddress addr(*address);
    SleConnectState connState = state == SLE_ACB_CONNECTED_STATE ? SLE_CONNECT_CONNECTED_STATE :
        (state == SLE_ACB_DISCONNECTED_STATE ? SLE_CONNECT_DISCONNECTED_STATE : SLE_CONNECT_ERROR_STATE);
    connService->callback_->OnConnectStateChange(connectId, addr, connState, pairState, reason);
}

void SleConnectService::SleConnectServiceCallback::OnConnectParamUpdateRequest(uint16_t connectId,
    SleHalErrorCode errCode, const SleConnectParam *param)
{
    auto connService = SleConnectService::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(connService, "connect service instance null error!");
    SLE_ASSERT_AND_RETURN_LOG(connService->callback_, "connect service callback null error!");
    connService->callback_->OnConnectParameterUpdate(connectId, *param, (SleErrorCode)errCode);
}

void SleConnectService::SleConnectServiceCallback::OnConnectParamUpdateComplete(uint16_t connectId,
    SleHalErrorCode errCode, const SleConnectionParamUpdateEvt *param)
{
    auto connService = SleConnectService::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(connService, "connect service instance null error!");
    SLE_ASSERT_AND_RETURN_LOG(connService->callback_, "connect service callback null error!");
    connService->callback_->OnConnectParamUpdateComplete(connectId, *param, (SleErrorCode)errCode);
}

void SleConnectService::SleConnectServiceCallback::OnAuthComplete(uint16_t connectId,
    const SleDeviceAddress *address, SleHalErrorCode errCode, const SleAuthInfo *info)
{
    auto connService = SleConnectService::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(connService, "connect service instance null error!");
    SLE_ASSERT_AND_RETURN_LOG(connService->callback_, "connect service callback null error!");
    SleAddress addr(*address);
    connService->callback_->OnAuthComplete(connectId, addr, *info, (SleErrorCode)errCode);
}

void SleConnectService::SleConnectServiceCallback::OnPairComplete(uint16_t connectId,
    const SleDeviceAddress *address, SleHalErrorCode errCode)
{
    auto connService = SleConnectService::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(connService, "connect service instance null error!");
    SLE_ASSERT_AND_RETURN_LOG(connService->callback_, "connect service callback null error!");
    SleAddress addr(*address);
    connService->callback_->OnPairComplete(connectId, addr, (SleErrorCode)errCode);
}

void SleConnectService::SleConnectServiceCallback::OnReadRssiComplete(uint16_t connectId, int8_t rssi,
    SleHalErrorCode errCode)
{
    auto connService = SleConnectService::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(connService, "connect service instance null error!");
    SLE_ASSERT_AND_RETURN_LOG(connService->callback_, "connect service callback null error!");
    connService->callback_->OnReadRssiComplete(connectId, rssi, (SleErrorCode)errCode);
}

void SleConnectService::SleConnectServiceCallback::OnSetPhyParamComplete(uint16_t connectId,
    SleHalErrorCode errCode, const SleConnectionPhyParam *param)
{
    SLE_LOGD("(service) OnSetPhyParamComplete in, connectId = %d, errCode = %d", connectId, errCode);
    SLE_LOGD("(service) phy: tx_format = %d, rx_format = %d, tx_phy = %d, rx_phy = %d, "
             "tx_pilot_density = %d, rx_pilot_density = %d, g_feedback = %d, t_feedback = %d",
             param->tx_format, param->rx_format, param->tx_phy, param->rx_phy,
             param->tx_pilot_density, param->rx_pilot_density, param->g_feedback, param->t_feedback);
    SleConnectHalSetMcsValue(connectId, SleConnectService::GetInstance()->connectConfig_.mcs);
}

SleConnectHalCallbacks SleConnectService::SleConnectServiceCallback::halCallback_;
SleConnectHalCallbacks *SleConnectService::SleConnectServiceCallback::BuildHalConnectCallback()
{
    SLE_LOGD("BuildHalConnectCallback In");
    halCallback_.OnConnectHalConnectStateChangeCb = OnConnectStateChange;
    halCallback_.OnConnectHalConnectParamUpdateReqCb = OnConnectParamUpdateRequest;
    halCallback_.OnConnectHalConnectParamUpdateCompleteCb = OnConnectParamUpdateComplete;
    halCallback_.OnConnectHalAuthCompleteCb = OnAuthComplete;
    halCallback_.OnConnectHalPairCompleteCb = OnPairComplete;
    halCallback_.OnConnectHalReadRssiCompleteCb = OnReadRssiComplete;
    halCallback_.OnConnectHalSetPhyParamCompleteCb = OnSetPhyParamComplete;
    return &halCallback_;
}

SleConnectService::SleConnectService()
{
    SLE_LOGD("SleConnectService In");
    SleHalErrorCode ret = InitSleConnectHal();
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_HAL_SUCCESS, "init sle connect hal failed!");
    serviceCallback_ = std::make_unique<SleConnectServiceCallback>();
    SLE_ASSERT_AND_RETURN_LOG(serviceCallback_, "create service callback ptr failed!");
    ret = SleConnectHalRegisterCallbacks(serviceCallback_->BuildHalConnectCallback());
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_HAL_SUCCESS, "register hal connect callback failed!");
    connCfgMgr_ = std::make_unique<SleConnectConfigManager>();
    SLE_ASSERT_AND_RETURN(connCfgMgr_ != NULL);
    SleErrorCode result = connCfgMgr_->GetConnectConfig(connectConfig_);
    SLE_ASSERT_AND_RETURN_LOG(result == SLE_HAL_SUCCESS, "GetConnectConfig failed!");
}

SleConnectService::~SleConnectService()
{
    SLE_LOGD("~SleConnectService In");
    SleHalErrorCode ret = DeinitSleConnectHal();
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_HAL_SUCCESS, "deinit sle connect hal failed!");
    serviceCallback_ = nullptr;
}

SleConnectService *SleConnectService::GetInstance()
{
    static SleConnectService instance;
    return &instance;
}

SleErrorCode SleConnectService::Connect(const SleAddress &address)
{
    SLE_LOGD("Connect In");
    SleDeviceAddress addr = address.ConvertToSleDeviceAddress();
    SleHalErrorCode ret = SleConnectHalConnect(&addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "connect device failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::Disconnect(const SleAddress &address)
{
    SLE_LOGD("Disconnect In");
    SleDeviceAddress addr = address.ConvertToSleDeviceAddress();
    SleHalErrorCode ret = SleConnectHalDisconnect(&addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "disconnect device failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::UpdateConnectParam(uint16_t connectId, const SleConnectParam *param)
{
    SLE_LOGD("UpdateConnectParam In");
    //set phy
    SleConnectionPhyParam phy_param = {0};
    phy_param.tx_phy = connectConfig_.phy;
    phy_param.rx_phy = connectConfig_.phy;
    phy_param.tx_format = connectConfig_.formatType;
    phy_param.rx_format = connectConfig_.formatType;
    phy_param.tx_pilot_density = connectConfig_.pilotDensity;
    phy_param.rx_pilot_density = connectConfig_.pilotDensity;
    phy_param.g_feedback = 0;
    phy_param.t_feedback = 0;
    SleHalErrorCode retPhy = SleConnectHalSetPhyParam(connectId, &phy_param);
    SLE_ASSERT_AND_RETURN_RET_LOG(retPhy == SLE_HAL_SUCCESS, retPhy, "set phy param failed!");

    SleConnectionParamUpdate connParam = {
        .connectId = connectId,
        .intervalMin = param->intervalMin,
        .intervalMax = param->intervalMax,
        .maxLatency = param->latency,
        .supervisionTimeout = param->supervisionTimeout
    };
    SleHalErrorCode ret = SleConnectHalUpdateConnParam(&connParam);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "update connect param failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::SendPairRequest(const SleAddress &address)
{
    SLE_LOGD("SendPairRequest In");
    SleDeviceAddress addr = address.ConvertToSleDeviceAddress();
    SleHalErrorCode ret = SleConnectHalSendPairReq(&addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "send pair request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::RemovePairedDevice(const SleAddress &address)
{
    SLE_LOGD("RemovePairedDevice In");
    SleDeviceAddress addr = address.ConvertToSleDeviceAddress();
    SleHalErrorCode ret = SleConnectHalRemovePair(&addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "remove paired device failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::RemoveAllPairedDevices()
{
    SLE_LOGD("RemoveAllPairedDevices In");
    SleHalErrorCode ret = SleConnectHalRemoveAllPairs();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "remove all paired device failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::GetPairedDevicesNum(uint16_t &number)
{
    SLE_LOGD("GetPairedDevicesNum In");
    SleHalErrorCode ret = SleConnectHalGetPairedDevicesNum(&number);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "get paired device number failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::GetPairedDevices(std::vector<SleAddress> &devices, uint16_t &number)
{
    SLE_LOGD("GetPairedDevices In");
    SleDeviceAddress pairedDevices[SLE_MAX_CONNECT_ID_NUM] = {};
    SleHalErrorCode ret = SleConnectHalGetPairedDevices(pairedDevices, &number);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "get paired devices failed!");
    for (int i = 0; i < number; i++) {
        devices.emplace_back(SleAddress(pairedDevices[i]));
    }
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::GetPairState(const SleAddress &address, uint8_t &state)
{
    SLE_LOGD("GetPairState In");
    SleDeviceAddress addr = address.ConvertToSleDeviceAddress();
    SlePairState pairState;
    SleHalErrorCode ret = SleConnectHalGetPairState(&addr, &pairState);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "get paired state failed!");
    state = pairState;
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::ReadRemoteRssi(uint16_t connectId)
{
    SLE_LOGD("ReadRemoteRssi In");
    SleHalErrorCode ret = SleConnectHalReadRemoteRssi(connectId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "read remote rssi failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::SetDefaultConnParam(const SleDefaultConnectionParam &defaultConnParam)
{
    SLE_LOGD("SetDefaultConnParam In");
    SleHalErrorCode ret = SleConnectHalSetDefaultConnParam(&defaultConnParam);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_HAL_SUCCESS, SLE_ERR_FAILED, "set default connect param failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::RegisterConnectCallback(IConnectCallback &callback)
{
    SLE_LOGD("RegisterConnectCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback_ != &callback, SLE_ERR_INVALID_PARAM, "callback already registered error!");
    callback_ = &callback;
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectService::UnregisterConnectCallback(IConnectCallback &callback)
{
    SLE_LOGD("UnregisterConnectCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback_ == &callback, SLE_ERR_INVALID_PARAM, "callback already unregistered error!");
    callback_ = nullptr;
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS